/*
 * XMLSchemaValidationFilter.java
 *
 * Created on June 12, 2005, 11:16 AM
 *
 * (C) R. Alexander Milowski alex@milowski.com
 */

package org.infoset.xml.xerces;

import java.io.*;
import java.util.*;
import java.net.*;
import java.util.logging.*;

import org.infoset.xml.*;
import org.infoset.xml.filter.*;


import org.apache.xerces.xni.*;
import org.apache.xerces.xni.parser.*;
import org.apache.xerces.impl.XMLErrorReporter;
import org.apache.xerces.impl.XMLEntityManager;
import org.apache.xerces.impl.validation.*;
import org.apache.xerces.impl.xs.XMLSchemaValidator;
import org.apache.xerces.impl.xs.XSMessageFormatter;
import org.apache.xerces.util.SymbolTable;

/**
 *
 * @author R. Alexander Milowski
 */
public class XMLSchemaValidationFilter extends Item2DocumentHandler implements ItemFilter
{
   protected ItemDestination output;
   protected XMLSchemaValidator validator;

   Map namespaceMapping;
   XMLEntityResolver resolver;
   List errors;
   XMLErrorReporter reporter;
   XMLComponentManager manager;
   XMLEntityManager entityManager;
   ValidationManager validationManager;
   
   class LocalXMLComponentManager implements XMLComponentManager {
      public Object getProperty(String uri) {
         //System.out.println("property: "+uri);
         if (uri.equals(XMLSchemaValidator.ERROR_REPORTER)) {
            return reporter;
         } else if (uri.equals(XMLSchemaValidator.ENTITY_RESOLVER)) {
            return resolver;
         } else if (uri.equals("http://apache.org/xml/properties/internal/entity-manager")) {
            return entityManager;
         } else if (uri.equals("http://apache.org/xml/properties/schema/external-noNamespaceSchemaLocation")) {
            URL loc = (URL)namespaceMapping.get(Name.NO_NAMESPACE);
            return loc==null ? null : loc.toString();
         } else if (uri.equals("http://apache.org/xml/properties/internal/symbol-table")) {
            return symbolTable;
         } else if (uri.equals("http://apache.org/xml/properties/internal/validation-manager")) {
            return validationManager;
         }
         return null;
      }
      
      public boolean getFeature(String uri) {
         //System.out.println("feature: "+uri);
         if (uri.equals("http://xml.org/sax/features/validation")) {
            return true;
         } else if (uri.equals("http://apache.org/xml/features/validation/schema")) {
            return true;
         } else if (uri.equals("http://apache.org/xml/features/validation/schema/augment-psvi")) {
            return true;
         } else if (uri.equals("http://apache.org/xml/features/internal/parser-settings")) {
            return true;
         } else if (uri.equals("http://apache.org/xml/features/validation/schema-full-checking")) {
            return true;
         }
         return false;
      }
   }
   
   class LocalXMLErrorHandler implements XMLErrorHandler {
      public void error(String domain, String key, XMLParseException ex) throws XNIException
      {
         errors.add(ex.getMessage());
         if (finerLog) {
            ex.printStackTrace();
         }
      }

      public void fatalError(String domain, String key, XMLParseException ex) throws XNIException
      {
         errors.add(ex.getMessage());
         if (finerLog) {
            ex.printStackTrace();
         }
      }

      public void warning(String domain, String key, XMLParseException ex) throws XNIException
      {
         errors.add(ex.getMessage());
         if (finerLog) {
            ex.printStackTrace();
         }
      }
      
   }
   
   class LocalEntityResolver implements XMLEntityResolver {
      public XMLInputSource resolveEntity(XMLResourceIdentifier xmlResourceIdentifier) throws XNIException, java.io.IOException
      {
         try {
            if (fineLog) {
               log.fine("resolveEnity:");
               log.fine("  namespace='"+xmlResourceIdentifier.getNamespace()+'\'');
               log.fine("  baseSystemId='"+xmlResourceIdentifier.getBaseSystemId()+'\'');
               log.fine("  literalSystemId='"+xmlResourceIdentifier.getLiteralSystemId()+'\'');
               log.fine("  expandedSystemId='"+xmlResourceIdentifier.getExpandedSystemId()+'\'');
               log.fine("  publicId='"+xmlResourceIdentifier.getPublicId()+'\'');
            }
            String ns = xmlResourceIdentifier.getNamespace();
            URL location = null;
            String systemId = xmlResourceIdentifier.getExpandedSystemId();
            if (systemId!=null) {
               if (fineLog) {
                  log.fine("Using system id: "+systemId);
               }
               location = new URL(systemId);
            } else if (ns!=null || systemId==null) {
               if (fineLog) {
                  log.fine("Looking up namespace: "+ns);
               }
               URI key = ns==null ? Name.NO_NAMESPACE : new URI(ns);
               location = (URL)namespaceMapping.get(key);
               if (location==null) {
                  return null;
               } else if (fineLog) {
                  log.fine("Found location: "+location);
               }
            }
               
            InputStream in = location.openStream();
            XMLInputSource source = new XMLInputSource(xmlResourceIdentifier);
            source.setSystemId(location.toString());
            source.setByteStream(in);
            return source;
         } catch (URISyntaxException ex) {
            throw new XNIException(ex.getMessage(),ex);
         }
      }
   }
   
   /** Creates a new instance of XMLSchemaValidationFilter */
   public XMLSchemaValidationFilter()
   {
      output = null;
      namespaceMapping = new HashMap();
      validator = null;
      reporter = new XMLErrorReporter();
      reporter.setProperty("http://apache.org/xml/properties/internal/error-handler", new LocalXMLErrorHandler());
      reporter.putMessageFormatter(XSMessageFormatter.SCHEMA_DOMAIN,new XSMessageFormatter());
      manager = new LocalXMLComponentManager();
      validationManager = new ValidationManager();
      resolver = new LocalEntityResolver();
      entityManager = new XMLEntityManager();
      entityManager.setProperty("http://apache.org/xml/properties/internal/error-handler", new LocalXMLErrorHandler());
      entityManager.setProperty("http://apache.org/xml/properties/internal/entity-resolver", resolver);
      errors = new ArrayList();
   }
   
   public void attach(ItemDestination output) {
      
      this.output = output;
      reset();
   }
   
   public void addNamespaceMap(URI namespace,URL location) {
      namespaceMapping.put(namespace,location);
      if (namespace==Name.NO_NAMESPACE && validator!=null) {
         validator.setProperty("http://apache.org/xml/properties/schema/external-noNamespaceSchemaLocation", location.toString());
      }
   }
   
   public void addNamespaceMappings(Map mappings) {
      namespaceMapping.putAll(mappings);
      URL loc = (URL)namespaceMapping.get(Name.NO_NAMESPACE);
      if (loc!=null && validator!=null) {
         validator.setProperty("http://apache.org/xml/properties/schema/external-noNamespaceSchemaLocation", loc.toString());
      }
   }
   
   public void removeNamespace(URI namespace) {
      namespaceMapping.remove(namespace);
   }
   
   public void reset() {
      errors.clear();
      validator = new XMLSchemaValidator();
      validator.setDocumentHandler(new DocumentHandler2Item(output));
      setDocumentHandler(validator);
      validator.reset(manager);
   }
   
   public Iterator getErrors() {
      return errors.iterator();
   }
   
}
